package test.jdkapi.juc.custom_aqs;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * <pre>
 * 共享：重写 
 *      tryAcquireShared(n) 返回 >= 0，表示获取成功 
 *      和 
 *      tryReleaseShared(n) 返回 true，表示释放成功
 * 调用：
 *      获取锁：acquireShared(n)
 *      释放锁：releaseShared(n)
 * 注意：
 *      AQS.setHeadAndPropagate(h, i) 设置当前节点为头，并调用 AQS.doReleaseShared() 去唤醒
 *      AQS.doReleaseShared() 快的话，只循环一次；否则会不停的并发循环唤醒头的下一个节点
 * </pre>
 * 
 * <p>
 * Created by zengxf on 2018-02-24
 */
public class TwinsLock implements Lock {
    private final Sync sync = new Sync( 2 );

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -7889272986162341211L;

        Sync( int count ) {
            if ( count <= 0 ) {
                throw new IllegalArgumentException( "count must large than zero." );
            }
            setState( count );
        }

        public int tryAcquireShared( int reduceCount ) {
            for ( ;; ) {
                int current = getState();
                int newCount = current - reduceCount;
                if ( newCount < 0 || compareAndSetState( current, newCount ) ) {
                    return newCount;
                }
            }
        }

        public boolean tryReleaseShared( int returnCount ) {
            for ( ;; ) {
                int current = getState();
                int newCount = current + returnCount;
                if ( compareAndSetState( current, newCount ) ) {
                    return true;
                }
            }
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    }

    public void lock() {
        sync.acquireShared( 1 );
    }

    public void unlock() {
        sync.releaseShared( 1 );
    }

    public void lockInterruptibly() throws InterruptedException {
        sync.acquireSharedInterruptibly( 1 );
    }

    public boolean tryLock() {
        return sync.tryAcquireShared( 1 ) >= 0;
    }

    public boolean tryLock( long time, TimeUnit unit ) throws InterruptedException {
        return sync.tryAcquireSharedNanos( 1, unit.toNanos( time ) );
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}